using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO.Compression;
using System.Reflection;
using System.IO;
using System.Text;
using System.Web.Caching;

/// <summary>
/// Module that handles compression of JavaScript resources using
/// GZip and some basic code optimizations that strips full line
/// comments and whitespace from the beginning and end of lines.
///
/// This module should be used in conjunction with
/// ClientScriptProxy.RegisterClientScriptResource which sets
/// up the proper URL formatting required for this module to
/// handle requests. Format is (with base64 encode values):
///
/// </remarks>
/// </summary>

public class ScriptCompressionModule : IHttpHandler
{
    public static bool wwScriptCompressionModuleActive = false;

    public void ProcessRequest(HttpContext Context)
    {
        HttpRequest Request = Context.Request;
        // *** Skip over anything we don't care about immediately

        if (!Request.Url.LocalPath.ToLower().Contains("wwsc.axd"))

            return;
        HttpResponse Response = Context.Response;

        string AcceptEncoding = Request.Headers["Accept-Encoding"];
        // *** Start by checking whether GZip is supported by client

        bool UseGZip = false;

        if (!string.IsNullOrEmpty(AcceptEncoding) &&

            AcceptEncoding.ToLower().IndexOf("gzip") > -1)

            UseGZip = true;



        // *** Create a cachekey and check whether it exists

        string CacheKey = Request.QueryString.ToString() + UseGZip.ToString();
        byte[] Output = Context.Cache[CacheKey] as byte[];

        if (Output != null)
        {

            // *** Yup - read cache and send to client
            SendOutput(Output, UseGZip);
            return;
        }



        // *** Retrieve information about resource embedded

        // *** Values are base64 encoded

        string ResourceTypeName = Request.QueryString["t"];

        if (!string.IsNullOrEmpty(ResourceTypeName))

            ResourceTypeName = Encoding.ASCII.GetString(Convert.FromBase64String(ResourceTypeName));



        string Resource = Request.QueryString["r"];

        if (string.IsNullOrEmpty(Resource))
        {

            SendErrorResponse("Invalid Resource");

            return;

        }

        Resource = Encoding.ASCII.GetString(Convert.FromBase64String(Resource));



        // *** Try to locate the assembly that houses the Resource

        Assembly ResourceAssembly = null;



        // *** If no type is passed use the current assembly - otherwise

        // *** run through the loaded assemblies and try to find assembly

        if (string.IsNullOrEmpty(ResourceTypeName))

            ResourceAssembly = this.GetType().Assembly;

        else
        {

            ResourceAssembly = this.FindAssembly(ResourceTypeName);

            if (ResourceAssembly == null)
            {

                SendErrorResponse("Invalid Type Information");

                return;

            }

        }



        // *** Load the script file as a string from Resources

        string Script = "";

        using (Stream st = ResourceAssembly.GetManifestResourceStream(Resource))
        {

            StreamReader sr = new StreamReader(st);

            Script = sr.ReadToEnd();

        }
        
        // *** Don't GZip unless at least 8k

        if (UseGZip && Script.Length > 6000)

            Output = GZipMemory(Script);

        else
        {

            Output = Encoding.ASCII.GetBytes(Script);

            UseGZip = false;

        }
        // *** Add into the cache
        Context.Cache.Add(CacheKey, Output, null, DateTime.UtcNow.AddDays(1), TimeSpan.Zero, CacheItemPriority.High, null);
        // *** Write out to Response object with appropriate Client Cache settings

        this.SendOutput(Output, UseGZip);

    }





    /// <summary>
    /// Returns an error response to the client. Generates a 404 error
    /// </summary>
    /// <param name="Message"></param>

    private void SendErrorResponse(string Message)
    {

        if (!string.IsNullOrEmpty(Message))

            Message = "Invalid Web Resource";



        HttpContext Context = HttpContext.Current;



        Context.Response.StatusCode = 404;

        Context.Response.StatusDescription = Message;

        Context.Response.End();

    }



    /// <summary>

    /// Sends the output to the client using appropriate cache settings.

    /// Content should be already encoded and ready to be sent as binary.

    /// </summary>

    /// <param name="Output"></param>

    /// <param name="UseGZip"></param>

    private void SendOutput(byte[] Output, bool UseGZip)
    {

        HttpResponse Response = HttpContext.Current.Response;



        Response.ContentType = "application/x-javascript";

        if (UseGZip)

            Response.AppendHeader("Content-Encoding", "gzip");



        if (!HttpContext.Current.IsDebuggingEnabled)
        {

            Response.ExpiresAbsolute = DateTime.UtcNow.AddYears(1);

            Response.Cache.SetLastModified(DateTime.UtcNow);

            Response.Cache.SetCacheability(HttpCacheability.Public);

        }



        Response.BinaryWrite(Output);

        Response.End();

    }




    /// <summary>
    /// Finds an assembly in the current loaded assembly list
    /// </summary>
    /// <param name="TypeName"></param>
    /// <returns></returns>
    private Assembly FindAssembly(string TypeName)
    {

        foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
        {

            if (ass.FullName == TypeName)

                return ass;

        }



        return null;

    }
    
    /// <summary>
    /// Takes a binary input buffer and GZip encodes the input
    /// </summary>
    /// <param name="Buffer"></param>
    /// <returns></returns>
    public static byte[] GZipMemory(byte[] Buffer)
    {

        MemoryStream ms = new MemoryStream();
        GZipStream GZip = new GZipStream(ms, CompressionMode.Compress);
        GZip.Write(Buffer, 0, Buffer.Length);

        GZip.Close();
        byte[] Result = ms.ToArray();

        ms.Close();

        return Result;
    }





    public static byte[] GZipMemory(string Input)
    {

        return GZipMemory(Encoding.ASCII.GetBytes(Input));

    }


    public bool IsReusable
    {
        get { return true; }
    }

    
     
}